package student.thread.wait;

/**
 * wait和 notify方法要在同步块中调用，否者抛出IllegalMonitorStateException异常
 * wait: 释放锁，进入等待状态，notify：随机唤醒其它等待的一个线程
 *
 * 1. 一定在synchronized中使用wait()/notify()/notifyAll()，也就是说一定要先获取锁，这个前面我们讲过，因为只有加锁后，才能获得监视器。否则jvm也会抛出IllegalMonitorStateException异常。
 * 2. 使用wait()时，判断线程是否进入wait状态的条件一定要使用while而不要使用if，因为等待线程可能会被错误地唤醒，所以应该使用while循环在等待前等待后都检查唤醒条件是否被满足，保证安全性。
 * 3. notify()或notifyAll()方法调用后，线程不会立即释放锁。调用只会将wait中的线程从等待队列移到同步队列，也就是线程状态从waitting变为blocked;
 * 4. 从wait()方法返回的前提是线程重新获得了调用对象的锁。
 */
class WaitNotifyDemo {

    // 可以是任意一个对象，或者自定义的对象
    static Object o1 = new Object();

    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        b.start();
        System.out.println("b is start....");
        //主线程获取o1的对象锁
        synchronized (o1)
        {
            try {
                System.out.println("Waiting for b to complete...");
                o1.wait();//o1的对象锁释放，主线程进入等待状态
                System.out.println("Completed.Now back to main thread");
            } catch (InterruptedException e) {
            }
        }
        System.out.println("Total is :" + b.total);
    }

    static class ThreadB extends Thread {
        int total;
        @Override
        public void run() {
            synchronized (o1) {//ThreadB获取o1的对象锁
                System.out.println("ThreadB is running..");
                for (int i = 0; i < 5; i++) {
                    total += i;
                    System.out.println("total is " + total);
                }
                o1.notify();//ThreadB释放o1的对象锁，通知其他等待o1对象锁的线程继续运行
            }
        }
    }
}

